/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * timer.cc																	*
 *																			*
 * Hardware timer utilities, and the control center of the kernel: the		*
 * timer interrupt handler.													*
 *																			*
\***************************************************************************/

#include <system.h>
#include <console.h>
#include <task.h>
#include <pm.h>

/// Sets the clock rate, in hertz
void SetClockRate( int hz )
{
	int divisor = 1193180 / hz;       /* Calculate our divisor */
	outportb(0x43, 0x36);             /* Set our command byte 0x36 */
	outportb(0x40, divisor & 0xFF);   /* Set low byte of divisor */
	outportb(0x40, divisor >> 8);     /* Set high byte of divisor */
}

/// Tick timer
volatile unsigned int tick = 0;

/// Second counter
volatile unsigned int sc = 0;

/// Total available memory
extern unsigned long __mb_totmem;

/// Rate for the clock in hertz
#define clockrate 1000

/// Timer IRQ handler
void timer_handler( struct regs* r )
{
	// increment the tick timer
	tick++;
	
	// check for second
	if( tick % clockrate == 0 )
	{
		sc++;
	}
	
	// every 1/4 of a second, update the memory display
	if( tick % (clockrate/4) == 0 )
	{
		// SAVE the x/y locations
		unsigned int x = cons.getx();
		unsigned int y = cons.gety();
		
		// find out mem usage
		char* p = (char*) pm_alloc( 1 );
		unsigned int a = ((unsigned int) p) - 4096;
		pm_free( p );
		unsigned long memusage = ( __mb_totmem - a ) / 1024;
		
		// print it (give it 11 characters of space)
		cons.setx( 79 - 11 );
		cons.sety( 0 );
		kprintf( "%8d kb", memusage );
		
		// RESTORE the x/y locations
		cons.setx( x );
		cons.sety( y );
		//cons.MoveCursor();
	}
	
	// handle multitasking - task switch
	kprintf( "timer : " );
	if( mtenabled() ) { resched(); };
}

/// Installs the timer
void install_timer()
{
	// install the irq
	irq_install_handler( 0, timer_handler );
	
	// set the clock to tick every millisecond
	SetClockRate( clockrate );
}

/// Sleeps for a period of time
void sleep( int seconds )
{
	// start time
	unsigned int now = sc;
	
	// wait...
	while( sc - now != seconds );
}

/// Delays for a period of time
void delay( int ms )
{
	// start time
	unsigned int now = tick;
	
	// wait...
	while( tick - now != ms );
}

/// Clock's tick count (since boot)
unsigned int clock()
{
	return tick;
}
